package com.css.fxfzmhwz.common.utils;

import org.gdal.gdal.gdal;
import org.gdal.ogr.*;
import org.gdal.osr.SpatialReference;
import org.locationtech.jts.geom.Geometry;

import java.util.ArrayList;
import java.util.List;

/**
 * gdal读取gdb文件
 * @author zyt
 * @date 2022/8/8
 */
public class GdbDataUtil {

    public static void testGdbDataUtil() {
        //测试初始注册
        gdal.AllRegister();
        //将gdal支持的所有驱动类型打印出来，说明gdal支持这些数据的读写
        int count = ogr.GetDriverCount();
        for (int i = 0; i < count; i++) {
            String driverName = ogr.GetDriver(i).getName();
            System.out.print(driverName + "\t");
        }
        //销毁相应的驱动管理对象
        gdal.GDALDestroyDriverManager();

        /*String filePath = "E:\\work\\海西PGA点\\海西PGA点\\新建文件地理数据库.gdb";
        //测试读取GDB文件
        testReadGdb(filePath);*/
    }

    //测试读取GDB文件
    public static void testReadGdb(String filePath) {
        // 指定文件的名字和路径
        String gdbFile = filePath;
        // 注册所有的驱动
        ogr.RegisterAll();
        // 为了支持中文路径，请添加下面这句代码
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
        // 为了使属性表字段支持中文，请添加下面这句
        gdal.SetConfigOption("SHAPE_ENCODING", "CP936");

        // 读取数据，这里以gdb文件为例
        String strDriverName = "OpenFileGDB";
        // 创建一个文件，根据strDriverName扩展名自动判断驱动类型
        Driver oDriver = ogr.GetDriverByName(strDriverName);

        if (oDriver == null) {
            System.out.println(strDriverName + " 驱动不可用！\n");
            return;
        }
        DataSource dataSource = oDriver.Open(gdbFile, 0);

        for(int i = 0;i<dataSource.GetLayerCount();i++) {
            Layer layerIdx = dataSource.GetLayer(i);
            System.out.println("图层名称：<==>" + layerIdx.GetName());
        }

        Layer layer = dataSource.GetLayer("海西PGA");
        String layerName = layer.GetName();
        System.out.println("图层名称：" + layerName);
        SpatialReference spatialReference = layer.GetSpatialRef();
        //System.out.println(spatialReference);

        System.out.println("空间参考坐标系：" + spatialReference.GetAttrValue("AUTHORITY", 0)
                + spatialReference.GetAttrValue("AUTHORITY", 1));

        double[] layerExtent = layer.GetExtent();

        System.out.println("图层范围：minx:" + layerExtent[0] + ",maxx:" + layerExtent[1] + ",miny:" + layerExtent[2]
                + ",maxy:" + layerExtent[3]);

        FeatureDefn featureDefn = layer.GetLayerDefn();

        int fieldCount = featureDefn.GetFieldCount();

        List<String> fieldNameList = new ArrayList<String>();
        for (int i = 0; i < fieldCount; i++) {
            FieldDefn fieldDefn = featureDefn.GetFieldDefn(i);
            // 得到属性字段类型
            int fieldType = fieldDefn.GetFieldType();
            String fieldTypeName = fieldDefn.GetFieldTypeName(fieldType);
            // 得到属性字段名称
            String fieldName = fieldDefn.GetName();
            fieldNameList.add(fieldName);
        }

        System.out.println(fieldNameList);
        long featureCount = layer.GetFeatureCount();
        System.out.println("图层要素个数：" + featureCount);

        Feature feature2 = null;
        while((feature2 = layer.GetNextFeature()) != null) {
            //System.out.println(feature2.GetGeometryRef().ExportToJson());
            //System.out.println(feature2.GetFieldAsString("Shape_Area") + "\t" + feature2.GetFieldAsString("省区"));
            for(int i=0;i<fieldNameList.size();i++){
                String fieldName = fieldNameList.get(i);
                //根据属性字段名称获取属性值
                System.out.println(fieldName+"\t"+feature2.GetFieldAsString(fieldName));
            }
            //zyt
            org.gdal.ogr.Geometry geometry = feature2.GetGeometryRef();
            String wkt = geometry.ExportToWkt();
            System.out.println(wkt);

        }

        //销毁相应的驱动管理对象
        oDriver.delete();
        gdal.GDALDestroyDriverManager();
    }

    //读取GDB文件并校验--type上传类型（行政区划_）
    public static List<Object> readGDBFileCheck(String path, String codeType,String type, String boundariesCoordinate) {
        List<Object> result = new ArrayList<Object>();
        String lonLatStr = "", wkt = "";
        String resMsg = "校验成功";
        int count = 0;
        //校验空间分辨率不是6秒
        double lon = 0, lat = 0;
        /*Map<Double, List<Double>> lon2latitudes = new HashMap<>();
        Map<Double, List<Double>> lat2longitudes = new HashMap<>();*/
        /*List<String> allWktStrList = new ArrayList<String>();//全部空间信息字符串列表*/
        List<Double> allLonList = new ArrayList<Double>();
        List<Double> allLatList = new ArrayList<Double>();
        //校验是否为罗马数字
        String roman = "Ⅰ Ⅱ Ⅲ Ⅳ Ⅴ Ⅵ Ⅶ Ⅷ Ⅸ Ⅹ", arab = "1 2 3 4", level = "";//修改等级值范围为1-4
        String SCR_arab = "1 2 3 4 9 10";//场地类型
        //校验矢量面是否在省市县所在区域内
        List<List<com.css.fxfzmhwz.common.utils.entity.Polygon>> multiPolygonList = new ArrayList<List<com.css.fxfzmhwz.common.utils.entity.Polygon>>();
        List<Geometry> multiPolygonGeometryList = new ArrayList<Geometry>();
        //Geometry boundariesGeometry = null;//行政区域
        /*//只有矢量面判断是否在行政区划范围内
        if("RLMPolygon_".equals(type)){
            boundariesGeometry = getPolygonGeometryList(boundariesCoordinate);//行政区域
        }*/
        //boundariesGeometry = getPolygonGeometryList(boundariesCoordinate);//行政区域
        List<com.css.fxfzmhwz.common.utils.entity.Point[]> boundaries = VectorDataUtil.getDistrictBoundaryByString(boundariesCoordinate);
        //zyt-test
        boolean tempBool = true;//判断是否需要转换经纬度值

        // 指定文件的名字和路径
        //String gdbFile = "E:\\work\\海西PGA点\\海西PGA点\\新建文件地理数据库.gdb";
        String gdbFile = path;
        // 注册所有的驱动
        ogr.RegisterAll();
        // 为了支持中文路径，请添加下面这句代码
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
        // 为了使属性表字段支持中文，请添加下面这句
        gdal.SetConfigOption("SHAPE_ENCODING", "CP936");

        // 读取数据，这里以gdb文件为例
        String strDriverName = "OpenFileGDB";
        // 创建一个文件，根据strDriverName扩展名自动判断驱动类型
        Driver oDriver = ogr.GetDriverByName(strDriverName);

        if (oDriver == null) {
            System.out.println(strDriverName + " 驱动不可用！\n");
            return null;
        }
        DataSource dataSource = oDriver.Open(gdbFile, 0);

        for(int i = 0;i<dataSource.GetLayerCount();i++) {
            Layer layerIdx = dataSource.GetLayer(i);
            System.out.println("图层名称：<==>" + layerIdx.GetName());
        }

        //获取第一个图层
        Layer layer = dataSource.GetLayer(0);
        String layerName = layer.GetName();
        System.out.println("图层名称：" + layerName);
        SpatialReference spatialReference = layer.GetSpatialRef();
        //System.out.println(spatialReference);

        //空间参考坐标系
        String spatialReferenceStr = spatialReference.GetAttrValue("AUTHORITY", 0)
                + spatialReference.GetAttrValue("AUTHORITY", 1);
        System.out.println("空间参考坐标系：" + spatialReferenceStr);

        if(!spatialReferenceStr.equals("EPSG4490")){//判断是否为CGCS2000坐标系数据
            resMsg = "spatialReferenceError";
            //返回结果
            result.add(codeType);//编码类型（0：UTF-8，1：GBK）
            result.add(count);//数据长度
            result.add(resMsg);//校验结果
            return result;
        }

        double[] layerExtent = layer.GetExtent();

        System.out.println("图层范围：minx:" + layerExtent[0] + ",maxx:" + layerExtent[1] + ",miny:" + layerExtent[2]
                + ",maxy:" + layerExtent[3]);

        FeatureDefn featureDefn = layer.GetLayerDefn();

        int fieldCount = featureDefn.GetFieldCount();

        List<String> fieldNameList = new ArrayList<String>();
        for (int i = 0; i < fieldCount; i++) {
            FieldDefn fieldDefn = featureDefn.GetFieldDefn(i);
            // 得到属性字段类型
            int fieldType = fieldDefn.GetFieldType();
            String fieldTypeName = fieldDefn.GetFieldTypeName(fieldType);
            // 得到属性字段名称
            String fieldName = fieldDefn.GetName();
            fieldNameList.add(fieldName);
        }

        System.out.println(fieldNameList);
        long featureCount = layer.GetFeatureCount();
        System.out.println("图层要素个数：" + featureCount);

        Feature feature2 = null;
        while((feature2 = layer.GetNextFeature()) != null) {
            List<Object> attributes = new ArrayList<Object>();
            //获取空间信息
            org.gdal.ogr.Geometry geometry = feature2.GetGeometryRef();
            wkt = geometry.ExportToWkt();
            //System.out.println(wkt);
            attributes.add(wkt);//空间信息
            //System.out.println(feature2.GetGeometryRef().ExportToJson());
            for(int i=0;i<fieldNameList.size();i++){
                String fieldName = fieldNameList.get(i);
                //根据属性字段名称获取属性值
                //System.out.println(fieldName+"\t"+feature2.GetFieldAsString(fieldName));
                attributes.add(feature2.GetFieldAsString(fieldName));
            }

            if(count%100000==0){
                System.out.println(count);
            }
            //0.判断字段数量是否符合要求
            int size = fieldNameList.size() + 1;//空间属性加1
            if("PED_".equals(type)){
                if(size<11){
                    resMsg = "fieldNumError";
                    break;
                }
            }else if("RLM_".equals(type)){
                if(size<4){
                    resMsg = "fieldNumError";
                    break;
                }
            }else if("RLMPolygon_".equals(type)){
                if(size<2){
                    resMsg = "fieldNumError";
                    break;
                }
            }else if("SCR_".equals(type)){
                if(size<2){
                    resMsg = "fieldNumError";
                    break;
                }
            }
            /*if(tempBool){
                //wkt = attributes.get(0).toString();
                if (wkt.indexOf("MULTIPOINT") != -1) {
                    lonLatStr = reverseWktMultiPoint(ProjectTransUtil.wkt2lonLat(wkt));
                } else if (wkt.indexOf("POINT") != -1) {
                    lonLatStr = reverseWktPoint(ProjectTransUtil.wkt2lonLat(wkt));
                } else if (wkt.indexOf("MULTIPOLYGON") != -1) {
                    lonLatStr = reverseWktMultiPolygon(ProjectTransUtil.wkt2lonLat(wkt));
                } else if (wkt.indexOf("POLYGON") != -1) {
                    lonLatStr = reverseWktPolygon(ProjectTransUtil.wkt2lonLat(wkt));
                }
                attributes.set(0, lonLatStr);
                //判断是否经纬度值是否相等
                if(wkt.equals(lonLatStr)){
                    tempBool = false;//判断是否需要转换经纬度值
                }
            }*/
            //校验矢量面是否在省市县所在区域内
            if("RLMPolygon_".equals(type)){
                //根据属性获取矢量面列表
                List<com.css.fxfzmhwz.common.utils.entity.Polygon> polygonList = VectorDataUtil.getPolygonList(attributes);
                //校验数据是否在省市县所在区域边界内
                if(!VectorDataUtil.newCheckPointIsInDistrict(boundaries, polygonList)){
                    resMsg = "PointIsNotInDistrict";
                    break;
                }

                //添加到面经纬度列表用于校验面是否相交
                multiPolygonList.add(polygonList);
                //test
                Geometry polygon = VectorDataUtil.getPolygonGeometryList(attributes.get(0).toString());
                multiPolygonGeometryList.add(polygon);
            }
            if("PED_".equals(type) || "RLM_".equals(type)){
                //1.校验GeometryIsNotEqualsCoordinate
                String pointWkt = attributes.get(0).toString();
                String A = attributes.get(1).toString();
                String B = attributes.get(2).toString();
                /*if (!geometryIsEqualsCoordinate(pointWkt, A, B)) {
                    resMsg = "GeometryIsNotEqualsCoordinate";
                    break;
                }*/
                //新校验经纬度方法
                if (!VectorDataUtil.newGeometryIsEqualsCoordinate(pointWkt, A, B)) {
                    resMsg = "GeometryIsNotEqualsCoordinate";
                    break;
                }

                //可注释判断以下判断点是否在行政区划内代码
                /*//判断点是否在行政区划内
                if(!VectorDataUtil.newCheckPointIsInDistrictBoundary(boundaries,A,B)){
                    resMsg = "PointIsNotInDistrict";
                    break;
                }*/
                if(count<10){//仅判断10个点
                    //判断点是否在行政区划内
                    if(!VectorDataUtil.newCheckPointIsInDistrictBoundary(boundaries,A,B)){
                        resMsg = "PointIsNotInDistrict";
                        break;
                    }
                }
            }
            //2.校验是否有乱码
            int msssyIndex = -1;
            //校验是否有乱码
            for (int j = 0; j < attributes.size(); j++) {
                if(attributes.get(j)==null){
                    continue;
                }
                String value = attributes.get(j).toString();
                for (int k = 0; k < value.length() / 2; k++) {
                    char c = value.charAt(k);
                    // 当从Unicode编码向某个字符集转换时，如果在该字符集中没有对应的编码，则得到0x3f（即问号字符?）
                    //从其他字符集向Unicode编码转换时，如果这个二进制数在该字符集中没有标识任何的字符，则得到的结果是0xfffd
                    if ((int) c == 0xfffd) {
                        // 存在乱码
                        msssyIndex = count;
                        break;
                    }
                }
                if(msssyIndex!=-1){
                    resMsg = "messyCode#"+msssyIndex;
                    break;
                }
            }
            if(msssyIndex!=-1){
                break;
            }
            //3.校验字段空值
            for (int j = 0; j < attributes.size(); j++) {
                if(attributes.get(j)==null){
                    resMsg = "fieldNullValue";
                    break;
                }
                String value = attributes.get(j).toString();
                //校验字段空值
                if (value == null || "".equals(value.trim())){
                    resMsg = "fieldNullValue";
                    break;
                }
            }
            if(!resMsg.equals("校验成功")){
                break;
            }
            //上传类型（行政区划_）
            if ("PED_".equals(type)) {
                /*//4.校验空间分辨率不是6秒
                lon = Double.parseDouble(A);
                lat = Double.parseDouble(B);
                if (lon2latitudes.containsKey(lon)) {
                    lon2latitudes.get(lon).add(lat);
                } else {
                    List<Double> latitudes = new ArrayList<>();
                    latitudes.add(lat);
                    lon2latitudes.put(lon, latitudes);
                }
                if (lat2longitudes.containsKey(lat)) {
                    lat2longitudes.get(lat).add(lon);
                } else {
                    List<Double> longitudes = new ArrayList<>();
                    longitudes.add(lon);
                    lat2longitudes.put(lat, longitudes);
                }*/
                //4.校验空间分辨率不是6秒
                String A = attributes.get(1).toString();
                String B = attributes.get(2).toString();
                lon = Double.parseDouble(A);
                lat = Double.parseDouble(B);
                allLonList.add(lon);
                allLatList.add(lat);
                //校验是否空间位置重叠
                /*allWktStrList.add(attributes.get(0).toString());*/
            }else{
                //4.校验是否为罗马数字
                if ("RLM_".equals(type)) {
                    level = attributes.get(3).toString();
                    if (arab.indexOf(level) == -1){
                        resMsg = "levelError";
                        System.out.println("levelError:"+level);
                        break;
                    }
                } else if ("RLMPolygon_".equals(type)) {
                    level = attributes.get(1).toString();
                    if (arab.indexOf(level) == -1){
                        resMsg = "levelError";
                        System.out.println("levelError:"+level);
                        break;
                    }
                } else if ("SCR_".equals(type)) {
                    level = attributes.get(1).toString();
                    if (SCR_arab.indexOf(level) == -1 || level=="0"){
                        resMsg = "levelError";
                        System.out.println("levelError:"+level);
                        break;
                    }
                }
            }

            count++;
        }

        //销毁相应的驱动管理对象
        oDriver.delete();
        gdal.GDALDestroyDriverManager();

        System.out.println(count);
        //上传类型（行政区划_）
        if ("PED_".equals(type) && resMsg.equals("校验成功")) {
            /*//校验空间分辨率不是6秒
            if (!(isLessSixSecond(lon2latitudes) || isLessSixSecond(lat2longitudes))){
                resMsg = "SpatialResolutionError";
            }*/
            //校验空间分辨率不是6秒
            if (!(VectorDataUtil.isLessSixSecondDistance(allLonList,allLatList))){
                resMsg = "SpatialResolutionError";
            }
            /*//校验点空间位置是否不重叠
            if(!(VectorDataUtil.isPointNotOverlap(allWktStrList))){
                resMsg = "PointOverlapError";
            }*/
        }
        //校验矢量面是否在存在两两相交（或自相交）
        if("RLMPolygon_".equals(type) && resMsg.equals("校验成功")){
            //校验矢量面是否在存在自相交
            String resultStr = VectorDataUtil.checkIsPolygonsIntersect(multiPolygonList);
            if(resultStr!="校验成功"){
                resMsg = resultStr;
            }
            if(resMsg.equals("校验成功")){
                //校验矢量面是否在存在两两相交
                resultStr = VectorDataUtil.checkIsPolygonGeometrysIntersect(multiPolygonGeometryList);
                if(resultStr!="校验成功"){
                    resMsg = resultStr;
                }
            }
        }
        //返回结果
        result.add(codeType);//编码类型（0：UTF-8，1：GBK）
        result.add(count);//数据长度
        result.add(resMsg);//校验结果
        //校验是否有乱码
        if(resMsg.indexOf("messyCode")!=-1 && !codeType.equals("GBK")){
            result = readGDBFileCheck(path, "GBK",type, boundariesCoordinate);
        }

        return result;
    }
}